// -----------------------------------------------------------------------
//  <copyright file="SecurityStampValidationMiddleware.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2025 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2025-03-04 23:03</last-date>
// -----------------------------------------------------------------------

using DaprPlus.AspNetCore.Middleware;
using DaprPlus.Identity;

using Microsoft.AspNetCore.Http;

using OSharp.Security;


namespace DaprPlus.Authorization.OpenIddict;

/// <summary>
/// SecurityStamp验证中间件，用于验证Token中的user_version与OnlineUser的SecurityStamp是否匹配
/// 当用户权限变更（SecurityStamp更新）时，旧Token自动失效
/// </summary>
public class SecurityStampValidationMiddleware(RequestDelegate next, ILogger<SecurityStampValidationMiddleware> logger)
    : MiddlewareBase(next)
{
    private readonly RequestDelegate _next = next;

    /// <summary>
    /// 执行中间件拦截逻辑
    /// </summary>
    /// <param name="context">Http上下文</param>
    /// <returns></returns>
    public override async Task InvokeAsync(HttpContext context)
    {
        // 如果未认证（匿名访问），直接通过，不进行任何验证
        if (context.User.Identity?.IsAuthenticated != true)
        {
            await _next(context);
            return;
        }

        // 已认证的请求，需要进行SecurityStamp验证
        try
        {
            // 从Token Claims中获取user_version claim
            var userVersionClaim = context.User.FindFirst("user_version")?.Value;

            // 如果Token中没有user_version claim，返回401状态码
            if (string.IsNullOrEmpty(userVersionClaim))
            {
                logger.LogWarning("Token validation failed: Token does not contain user_version claim. User: {UserName}",
                    context.User.Identity.Name);
                context.Response.StatusCode = 401;
                context.Response.ContentType = "application/json";
                await context.Response.WriteAsync("{\"error\":\"invalid_token\",\"error_description\":\"Token does not contain user_version claim.\"}");
                return;
            }

            // 通过IOnlineUserProvider获取OnlineUser
            var onlineUserProvider = context.RequestServices.GetRequiredService<IOnlineUserProvider>();
            var onlineUser = await onlineUserProvider.GetAsync();

            // 如果OnlineUser为null，说明可能是匿名访问但携带了token，或者用户信息未缓存
            // 此时应该允许通过（匿名访问的功能不一定需要有在线用户）
            if (onlineUser == null)
            {
                logger.LogDebug("OnlineUser is null for authenticated user, allowing request to proceed. User: {UserName}",
                    context.User.Identity.Name);
                await _next(context);
                return;
            }

            // 如果OnlineUser.SecurityStamp为null或空，返回401状态码
            if (string.IsNullOrEmpty(onlineUser.SecurityStamp))
            {
                logger.LogWarning("Token validation failed: OnlineUser SecurityStamp is null or empty. UserId: {UserId}",
                    onlineUser.Id);
                context.Response.StatusCode = 401;
                context.Response.ContentType = "application/json";
                await context.Response.WriteAsync("{\"error\":\"invalid_token\",\"error_description\":\"User SecurityStamp is not available.\"}");
                return;
            }

            // 计算当前SecurityStamp的Hash
            var currentSecurityStampHash = HashHelper.GetSha256(onlineUser.SecurityStamp);

            // 与Token中的user_version进行对比
            if (currentSecurityStampHash != userVersionClaim)
            {
                logger.LogWarning("Token validation failed: SecurityStamp hash mismatch. UserId: {UserId}, TokenVersion: {TokenVersion}, CurrentVersion: {CurrentVersion}",
                    onlineUser.Id, userVersionClaim, currentSecurityStampHash);
                context.Response.StatusCode = 401;
                context.Response.ContentType = "application/json";
                await context.Response.WriteAsync("{\"error\":\"invalid_token\",\"error_description\":\"Token is invalid due to user information change. Please refresh your token.\"}");
                return;
            }

            // Hash匹配，验证通过，继续管道
            logger.LogDebug("SecurityStamp validation passed for user: {UserId}", onlineUser.Id);
            await _next(context);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Error occurred during SecurityStamp validation. User: {UserName}",
                context.User.Identity?.Name);
            // 发生异常时，为了安全起见，拒绝请求
            context.Response.StatusCode = 401;
            context.Response.ContentType = "application/json";
            await context.Response.WriteAsync("{\"error\":\"invalid_token\",\"error_description\":\"Token validation failed due to an internal error.\"}");
        }
    }
}

